/*题目:
  编程实现控制台版并支持两人对战的五子棋游戏
*/

import java.util.Scanner;

public class ChessGame {
    public static void main(String[] args) {

        System.out.println("欢迎来到五子棋游戏,现在为您绘制棋盘:");
        ChessGame game = new ChessGame();
        // 初始化一个 16*16 的二维数组, 用来储存棋盘的落子(下棋)情况
        // 数组中元素的默认值0表示未落子(没有下棋), 1表示黑棋落子(下了黑棋), 2表示白棋落子(下了白棋)
        int [][]arr = new int[16][16];
        // 1. 绘制棋盘 - draw方法实现
        game.draw(arr);
        // 2.进行游戏 (提示黑方和白方分别下棋并重新绘制棋盘) - play方法实现
        game.play(arr);
    }

    public void draw(int [][]arr) {
        for (int i = 0; i < 17; i++) {
            for (int j = 0; j < 17; j++) {
                // 第一行
                if (i == 0) {
                    // 第一行第一列为空
                    if (j == 0) {
                        System.out.print(" " + " ");
                    } else if (j < 11) {
                        // 按照数字 0-9的顺序，依次打印第一行中的 [1~10] 位元素
                        System.out.print((char) ('0' + j - 1) + " ");
                    } else {
                        // 按照字母 a-f的顺序，依次打印第一行中的 剩余元素
                        System.out.print((char) ('a' + j - 11) + " ");
                    }
                } else if (j == 0) {
                    // 第一列, 按照数字的顺序，打印第一列中的 [1~10] 位元素
                    if (i < 11) {
                        System.out.print((char) ('0' + i - 1) + " ");
                    } else {
                        // 按照字母的顺序，打印第一列中的 剩余元素
                        System.out.print((char) ('a' + i - 11) + " ");
                    }
                } else {
                    // 打印棋盘中可落子的元素
                    // 0为未落子, 在棋盘中用 + 表示
                    // 1为黑棋落子, 在棋盘中用 O 表示
                    // 2为白棋落子, 在棋盘中用 X 表示
                    // 由于draw函数接收的参数arr，仅用来表示棋盘的落子情况
                    // 不包含边框的元素, 因此, 下标的应该索引应为
                    // [i-1][j-1]
                    if (arr[i-1][j-1] == 0) {
                        System.out.print("+" + " ");
                    } else if (arr[i-1][j-1] == 1) {
                        System.out.print("O" + " ");
                    } else if (arr[i-1][j-1] == 2) {
                        System.out.print("X" + " ");
                    }
                }
            }
            System.out.println();
        }
    }

    public void play(int [][]arr){
        boolean isFinished; // isFinished 表示是否有任何一方获胜, 默认值为false
        boolean success;    // success 表示用户输入的棋子位置是否合法, 默认值为false
        int row = -1;       // row表示棋子位置的横坐标, 初始化为 -1
        int col = -1;       // col (column) 表示棋子位置的纵坐标, 初始化为 -1

        // 1.提示黑方下棋, 调用 playOneStep 方法来完成下棋的操作,
        //  第三个参数设置为true, 表示这个棋是黑棋
        //  (false 表示这个棋为白棋)
        System.out.println("请黑方输入您想要落子的位置(横坐标+空格+纵坐标):");
        success = playOneStep(row,col,arr,true);
        // 若playOneStep返回 true, 表示用户输入合法, 棋盘正常更新
        // 若playOneStep返回 false, 表示 用户的下棋位置不符合要求, 则需要重新下棋
        while(!success){
            success = playOneStep(row,col,arr,true);
        }


        // 2.判断是否获胜 - 使用 judge 方法实现
        isFinished = judge(arr);

        // 3. 向用户显示, 更新后的棋盘:
        System.out.println("此时的棋盘为：");
        draw(arr);
        // 如果黑棋已经胜出, 则游戏结束
        if(isFinished){
            return;
        }

        // 4. 提示白方下棋, 调用 playOneStep 方法,
        //    第三个参数设置为false, 表示这个棋是白棋
        System.out.println("请白方输入您想要落子的位置(横坐标+空格+纵坐标):");
        success = playOneStep(row,col,arr,false);
        // 同上, 如果没有下棋成功, 则用户重新输入
        while(!success){
            success = playOneStep(row,col,arr,false);
        }

        // 5. 判断是否获胜 - 使用 judge 方法实现
        isFinished = judge(arr);

        // 6. 向用户显示, 更新后的棋盘:
        System.out.println("此时的棋盘为：");
        draw(arr);

        // 7. 一轮结束之后, 若没有人胜出, 则继续调用play方法, 继续下棋
        if(!isFinished){
            play(arr);
        }
    }

    public boolean playOneStep(int row,int col,int[][] arr,boolean isBlack){

        Scanner sc = new Scanner(System.in);
        // 以空格为分界,将用户输入的字符串分割为两半,并存入 position[0]和 position[1]
        String[] position = sc.nextLine().split(" ");

        if ( (!isLetter(position[0].charAt(0))) && (!isLetter(position[1].charAt(0)))  ){
            // 横坐标和纵坐标，都不是字母 => 横坐标和纵坐标，都是数字 => 直接将char转化为数字
            row = Integer.parseInt(String.valueOf(position[0].charAt(0)));
            col = Integer.parseInt(String.valueOf(position[1].charAt(0)));
        }else if(isLetter((position[0].charAt(0))) && (!isLetter(position[1].charAt(0)))){
            // 横坐标是字母，纵坐标是数字
            row = letterToDigit(position[0].charAt(0));
            col = Integer.parseInt(String.valueOf(position[1].charAt(0)));
        }else if((!isLetter(position[0].charAt(0))) &&  (isLetter(position[1].charAt(0))) ){
            // 横坐标是数字，纵坐标是字母
            row = Integer.parseInt(String.valueOf(position[0].charAt(0)));
            col = letterToDigit(position[1].charAt(0));
        }else {
            // 横坐标和纵坐标，都是字母
            row = letterToDigit(position[0].charAt(0));
            col = letterToDigit(position[1].charAt(0));
        }

        // 判断输入的 row 和 col (横坐标 和 纵坐标)的值, 是否合法
        // 合法需要满足两个条件:
        // 1. 下棋位置没有越界 (在 0-15 的范围内)
        // 2. 下棋位置, 在下此棋之前, 不能有棋子
        if (isValid(row,col,arr)) {
            System.out.println("您在(" + position[0] + "," + position[1] + ") 位置下了一颗棋");
        } else{
            System.out.println("您输入的下棋位置不符合要求，请重新输入棋子的位置：");
            return false;
        }

        // 此时, 横坐标 和 纵坐标的值 都符合要求,  将棋盘更新
        if(isBlack){
            arr[row][col] = 1;
        }else{
            arr[row][col] = 2;
        }

        return true;
    }

    public boolean isValid(int row, int col,int[][] arr){
        // 1. 判断下棋位置, 是否越界 (在 0-15 的范围内)
        // 如果越界, 提示用户, 并返回false
        if( !(row >= 0 && row <= 15 && col >= 0 && col <= 15) ){
            System.out.println("该位置超出棋盘范围了!");
            return false;
        }

        // 2. 判断下棋位置, 在下此棋之前, 是否有棋子
        // 如果有棋子, 提示用户, 并返回false
        // 1 表示此位置已经下了黑棋, 2 表示此位置已经下了白棋
        if( arr[row][col] == 1 || arr[row][col] == 2 ){
            System.out.println("该位置已有棋子!");
            return false;
        }

        // 3. 同时满足条件1和条件2时，返回true
        return true;
    }

    public boolean judge(int [][] arr){
        int cntBlack = 0;
        int cntWhite = 0;

        // 1. 从左到右遍历棋盘, 检测是否出现获胜情况
        for (int i = 0; i < 16; i++) {
            for (int j = 0; j < 16; j++) {
                if (arr[i][j] == 1){
                    cntBlack ++;
                    cntWhite = 0;
                }else if (arr[i][j] == 2){
                    cntWhite ++;
                    cntBlack = 0;
                }

                if(cntBlack == 5){
                    System.out.println("游戏结束,黑棋获胜!");
                    return true;
                }else if (cntWhite == 5){
                    System.out.println("游戏结束,白棋获胜!");
                    return true;
                }
            }
            cntWhite = cntBlack = 0;
        }

        // 2. 从上到下遍历棋盘, 检测是否出现获胜情况
        for (int i = 0; i < 16; i++) {
            for (int j = 0; j < 16; j++) {
                if (arr[j][i] == 1){
                    cntBlack ++;
                    cntWhite = 0;
                }else if (arr[j][i] == 2){
                    cntWhite ++;
                    cntBlack = 0;
                }

                if(cntBlack == 5){
                    System.out.println("游戏结束,黑棋获胜!");
                    return true;
                }else if (cntWhite == 5){
                    System.out.println("游戏结束,白棋获胜!");
                    return true;
                }
            }
            cntWhite = cntBlack = 0;
        }

        // 3. 从左上到右下扫描棋盘, 检测是否出现获胜情况
        boolean isFinished;
        for (int n = 5; n <= 16; n++) {
            isFinished = checkDiagonal1(n,arr);
            if(isFinished){
                return true;
            }
        }

        // 4. 从右上到左下扫描棋盘, 检测是否出现获胜情况
        for (int n = 5; n <= 16; n++) {
            isFinished = checkDiagonal2(n,arr);
            if(isFinished){
                return true;
            }
        }

        return false;
    }

    public boolean checkDiagonal1(int n,int[][] arr){
        int cntWhite = 0; int cntBlack = 0;

        for (int i = 0; i < n; i++) {
            if(arr[i][i+ (16-n)] == 1){
                cntBlack ++;
                cntWhite = 0;
            }else if(arr[i][i+ (16-n) ] == 2){
                cntWhite ++;
                cntBlack = 0;
            }

            if(cntBlack == 5){
                System.out.println("游戏结束,黑棋获胜!");
                return true;
            }else if (cntWhite == 5){
                System.out.println("游戏结束,白棋获胜!");
                return true;
            }
        }

        cntBlack = cntWhite = 0;

        for (int i = 0; i < n; i++) {
            if(arr[i+ (16-n)][i] == 1){
                cntBlack ++;
                cntWhite = 0;
            }else if(arr[i+ (16-n)][i] == 2){
                cntWhite ++;
                cntBlack = 0;
            }

            if(cntBlack == 5){
                System.out.println("游戏结束,黑棋获胜!");
                return true;
            }else if (cntWhite == 5){
                System.out.println("游戏结束,白棋获胜!");
                return true;
            }
        }
        return false;
    }

    public boolean checkDiagonal2(int n,int[][] arr){
        int cntWhite = 0; int cntBlack = 0;

        for (int i = 0; i < n; i++) {
            if(arr[i][n -(i+1)] == 1){
                cntBlack ++;
                cntWhite = 0;
            }else if(arr[i][n -(i+1) ] == 2){
                cntWhite ++;
                cntBlack = 0;
            }

            if(cntBlack == 5){
                System.out.println("游戏结束,黑棋获胜!");
                return true;
            }else if (cntWhite == 5){
                System.out.println("游戏结束,白棋获胜!");
                return true;
            }
        }

        cntBlack = cntWhite = 0;

        for (int i = 0; i < n; i++) {
            if(arr[n -(i+1)][i] == 1){
                cntBlack ++;
                cntWhite = 0;
            }else if(arr[n -(i+1)][i] == 2){
                cntWhite ++;
                cntBlack = 0;
            }

            if(cntBlack == 5){
                System.out.println("游戏结束,黑棋获胜!");
                return true;
            }else if (cntWhite == 5){
                System.out.println("游戏结束,白棋获胜!");
                return true;
            }
        }
        return false;
    }

    // 方法: 判断字符 是否属于 a-z 范围中的字母
    public boolean isLetter(char c){
        if(c >= 'a' && c <= 'z'){
            return true;
        }else{
            return false;
        }
    }

    // 方法: 将 字符 a-f 转换为棋盘中相对应的 下棋位置 （10 - 15）
    // 若字符不在 a-f的范围内, 则返回 -1, 表示在棋盘中 没有 相对应的 下棋位置
    public int letterToDigit(char c){
        int val = -1;
        switch (c){
            case 'a':
                val = 10;
                break;
            case 'b':
                val = 11;
                break;
            case 'c':
                val = 12;
                break;
            case 'd':
                val = 13;
                break;
            case 'e':
                val = 14;
                break;
            case 'f':
                val = 15;
                break;
        }
        return val;
    }
}